﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using CS.Diagnostics;

namespace CS.Beehive
{

    /// <summary>
    /// 高性能TcpServer
    /// </summary>
    public class TcpServer : TcpServerSetting, IDisposable
    {


        #region 变量 属性

        //internal ChannelDisconnectMission DisconnectMission;

        /// <summary>
        /// 是否正在运行
        /// </summary>
        protected bool IsRunning;
        /// <summary>
        /// 是否暂停状态
        /// </summary>
        protected bool IsPause;

        /// <summary>
        ///   侦听连接请求的Socket
        /// </summary>
        protected Socket ListenSocket;

        /// <summary>
        /// 客户端连接次数计数器
        /// <remarks>服务启动后，每一个新的连接加入，+1</remarks>
        /// </summary>
        protected long ClientCounter;

        /// <summary>
        /// 连接会话集合(活动连接) , TODO:TCPChannel待改为接口
        /// </summary>
        protected readonly ConcurrentDictionary<long, ITcpChannel> SessionTable;

        /// <summary>
        /// 连接队列
        /// </summary>
        protected readonly ConnectionQueue ConnectingQueue;
        /// <summary>
        /// 连接池
        /// </summary>
        protected readonly ConnectionPool ConnectingPool;


        /// <summary>
        /// 连接数(客户端数量)
        /// </summary>
        public int Connections
        {
            get { return SessionTable.Count; }
        }


        //TODO：将读写池移至Server中分配给TcpChannel
        ///// <summary>
        ///// 读写池
        ///// </summary>
        //public ConnectSocketAsyncPool ReadWritePool { get; internal set; }

        #region 私有变量

        private readonly ConnectingEventArgs _connectingEventArgs; //连接发生时事件


        #endregion


        #endregion


        #region 构造及初始化 析构 Disposed

        /// <summary>
        /// 创建一个服务实例
        /// <remarks>只能通过TcpFactory实例化，禁止外部实例化。</remarks>
        /// </summary>
        internal TcpServer(TcpServerSetting setting)
        {
            ClientCounter = 0;
            SessionTable = new ConcurrentDictionary<long, ITcpChannel>();

            MaxConnections = setting.MaxConnections;
            SameIpMaxConnections = setting.SameIpMaxConnections;
            BufferSize = setting.BufferSize;
            ReadWritePoolSize = setting.ReadWritePoolSize;
            IsLittleEndian = true;

            ConnectingPool = new ConnectionPool(setting.ConnectPoolSize);
            ConnectingQueue = new ConnectionQueue();
            //ConnectingQueue = new ConcurrentQueue<Socket>();
            //ReadWritePool = new ConnectSocketAsyncPool(ReadWritePoolSize);
            //DisconnectMission = new ChannelDisconnectMission();//断开连接任务
            //DisconnectMission.Start(50);


            //私有变量初始化
            _connectingEventArgs = new ConnectingEventArgs(this);

        }


        #region IDisposable 成员

        /// <summary>
        /// TODO:未完成
        /// </summary>
        public void Dispose()
        {
            lock (this)
            {
                if (IsRunning)
                {
                    IsRunning = false;
                    SendToAll("I WILL BE BACK.".ToBytes());
                    CloseAllSession();
                    if (ListenSocket != null)
                    {
                        ReleaseSocket(ListenSocket);
                        ListenSocket = null;
                    }
                    //Connecting = null;

                    ConnectingQueue.Clear();

                    _connectingEventArgs.Server = null;
                }
            }
        }

        #endregion


        #endregion


        #region 公有方法


        #region Open 开启侦听   Stop   Pause     Resume   GetChannel

        /// <summary>
        /// 开启针对port端口的所有IP侦听
        /// </summary>
        /// <param name="port"></param>
        public virtual void Start(int port)
        {
            ServerIp = IPAddress.Any;//更改侦听的范围
            Start(new IPEndPoint(IPAddress.Any, port));
        }

        /// <summary>
        /// 开启针对ipEndPoint的侦听
        /// </summary>
        /// <param name="ipEndPoint"></param>
        public virtual void Start(IPEndPoint ipEndPoint)
        {
            Start(ipEndPoint, ConnectPoolSize);
        }

        /// <summary>
        /// 开启服务端ip:port的侦听
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public virtual void Start(string ip, int port)
        {
            Start(ip, port, ConnectPoolSize);
        }

        /// <summary>
        /// 开启侦听
        /// </summary>
        /// <param name="ip">*表示所有IP</param>
        /// <param name="port"></param>
        /// <param name="backlog"></param>
        public virtual void Start(string ip, int port, int backlog)
        {
            ServerIp = ip == "*" ? IPAddress.Any : IPAddress.Parse(ip);
            ConnectPoolSize = backlog;
            Start(new IPEndPoint(ServerIp, port), ConnectPoolSize);
        }

        /// <summary>
        /// 开启针对ipEndPoint的侦听
        /// </summary>
        /// <param name="ipEndPoint"></param>
        /// <param name="backlog"></param>
        public virtual void Start(IPEndPoint ipEndPoint, int backlog)
        {
            if (ListenSocket != null)
            {
                Dispose();
            }
            //创建侦听Sokect
            ListenSocket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
            {
                ReceiveBufferSize = BufferSize,
                SendBufferSize = BufferSize
            };
            ListenSocket.Bind(ipEndPoint);
            ListenSocket.Listen(128);
            IsRunning = true;
            //OnTrace("Server[{0}] is listening. ", ipEndPoint);
            //Tracer.Debug("Server[{0}] is listening.",ipEndPoint);
            Task.Factory.StartNew(ConnectHandling);
            Task.Factory.StartNew(BeginAccept);
        }

        /// <summary>
        ///   停止服务
        /// </summary>
        public void Stop()
        {
            if (!IsRunning) return;
            CloseAllSession();
            ReleaseSocket(ListenSocket);    //释放当前侦听客户端的Socket
            IsRunning = false;
        }

        /// <summary>
        /// 暂停
        /// </summary>
        public void Pause()
        {
            lock (SyncLock)
            {
                if (!IsPause)
                {
                    IsPause = true;
                }
            }
        }

        /// <summary>
        /// 从暂停中恢复
        /// </summary>
        public void Resume()
        {
            lock (SyncLock)
            {
                if (!IsPause) return;
                IsPause = false;
                if (!_isSyncAcceptWorking)
                {
                    Task.Factory.StartNew(BeginAccept);
                }
            }
        }

        /// <summary>
        /// 返回一个在线的客户端Channel
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public ITcpChannel GetClient(long clientId)
        {
            ITcpChannel channel;
            SessionTable.TryGetValue(clientId, out channel);
            return channel;
        }


        #region 侦听连接的内部方法

        /// <summary>
        /// 是否正在同步受理连接
        /// </summary>
        private bool _isSyncAcceptWorking;
        /// <summary>
        /// 开始接受客户端连接
        /// <remarks>内有循环</remarks>
        /// </summary>
        private void BeginAccept()
        {
            try
            {
                var isSync = true; //是否为异步连接
                while (IsRunning && isSync && !IsPause)
                {
                    var args = ConnectingPool.Pop();
                    args.Completed += AcceptCompleted;
                    try
                    {
                        isSync = !ListenSocket.AcceptAsync(args);  //true时为异步发送（由SocketAsyncEventArgs.Completed处理）；false即同步发送成功，要立即处理。
                        //Tracer.Debug("Connect is Sync:{0}",isSync);
                        _isSyncAcceptWorking = true;
                    }
                    catch (Exception ex)
                    {
                        OnException(ex);
                        return;
                    }
                    finally
                    {
                        if (isSync) ProcessAccept(args); //同步完成立即处理
                    }
                }
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
        }
        /// <summary>
        /// 接受完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void AcceptCompleted(object sender, SocketAsyncEventArgs args)
        {
            ProcessAccept(args);
            BeginAccept();
        }
        /// <summary>
        /// 同步接受数据
        /// </summary>
        /// <param name="args"></param>
        private void ProcessAccept(SocketAsyncEventArgs args)
        {
            try
            {
                if ((args.SocketError == SocketError.Success) && !IsPause)
                {
                    ConnectingQueue.Enqueue(args.AcceptSocket);
                }
                else
                {
                    ReleaseSocket(args.AcceptSocket);
                }
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
            finally
            {
                _isSyncAcceptWorking = false;
                //清理并返回池
                args.AcceptSocket = null;
                args.Completed -= AcceptCompleted;
                ConnectingPool.Push(args);
            }
        }

        /// <summary>
        /// 处理客户端的连接
        /// <remarks>循环工作</remarks>
        /// </summary>
        private void ConnectHandling()
        {
            while (IsRunning)
            {
                try
                {
                    var clientSocket = ConnectingQueue.Dequeue();
                    if (clientSocket != null)
                    {
                        //Tracer.Debug("get connecting socket from queue.");
                        //var eArgs = new ConnectingEventArgs(this, clientSockeet);
                        _connectingEventArgs.Cancel = false;
                        _connectingEventArgs.ClientSocket = clientSocket;

                        OnConnecting(_connectingEventArgs);//准备开始连接创建，如果有什么要做的，使用事件来做吧。比如IP限定啥的
                        if (_connectingEventArgs.Cancel)
                            DisconnectSocket(clientSocket); //被事件取消了
                        else
                            CreatingChannel(clientSocket);//创建连接通道
                    }
                    else
                    {
                        Thread.Sleep(20);
                    }
                }
                catch (Exception exception)
                {
                    OnException(exception);
                }
            }
        }

        /// <summary>
        /// 关闭Socket且不再使用
        /// </summary>
        /// <param name="connectingSocket"></param>
        private static void DisconnectSocket(Socket connectingSocket)
        {
            try
            {
                connectingSocket.Disconnect(false); //关闭且不再重复使用该Socket
            }
            catch (Exception ex)
            {
                Tracer.Error(ex);
                //throw;
            }
            finally
            {
                ReleaseSocket(connectingSocket);
            }
        }


        #region 会话相关方法


        /// <summary>
        /// 创建会话连接
        /// </summary>
        /// <param name="connectingSocket"></param>
        private void CreatingChannel(Socket connectingSocket)
        {
            var isMuchConnections = Connections >= MaxConnections;
            if (isMuchConnections || CheckHasMaxSameIp(connectingSocket))
            {
                //TODO:通知还是不通知客户端呢？
                OnException(new Exception(isMuchConnections ? "connection is too much!" : "sameip connection is too much!"));
                ReleaseSocket(connectingSocket);//直接关闭的客户端
            }
            else
            {
                if (Monitor.TryEnter(AddSyncLock, 500))
                {
                    var clientId = Interlocked.Increment(ref ClientCounter);
                    var channel = new TcpChannel(clientId, connectingSocket, this) { IsLittleEndian = IsLittleEndian };
                    OnConnected(channel); //连接创建完成,
                }
                else
                {
                    ReleaseSocket(connectingSocket);
                    Tracer.Warn("ClientId={0}[{1}] is disconnected,thread lock  is failed.", connectingSocket.RemoteEndPoint);
                }
            }
        }

        /// <summary>
        /// 增加会话
        /// </summary>
        /// <param name="channel"></param>
        private void AddChannel(ITcpChannel channel)
        {
            if (channel == null) return;
            if (Monitor.TryEnter(AddSyncLock, 500))
            {
                channel.Server = this;
                //channel.ChannelDisconnecting += OnDisconnecting;
                channel.ChannelDisconnected += OnClientDisconnected;
                //Tracer.Debug("ClientId={0} has  added event that client is disconnected .", channel.ClientId);
                SessionTable[channel.ClientId] = channel;
            }
            else
            {
                OnException(new TcpException(string.Format("can't add the channel[CliendId={0}:{1}]", channel.ClientId, channel.EndPoint)));
            }
        }
        /// <summary>
        /// 移除会话
        /// </summary>
        /// <param name="channel"></param>
        private void RemoveChannel(ITcpChannel channel)
        {
            if (channel == null) return;
            Tracer.Debug("ClientId={0} is removing.",channel.ClientId);
            if (Monitor.TryEnter(RemoveSyncLock, 500))
            {
                if (!SessionTable.TryRemove(channel.ClientId, out channel)) return;
                channel.Disconnect();
                channel.Server = null;
                //channel.ChannelDisconnecting -= OnDisconnecting;
                channel.ChannelDisconnected -= OnClientDisconnected;
            }
            else
            {
                OnException(new TcpException(string.Format("can't remove the channel[CliendId={0}:{1}]", channel.ClientId, channel.EndPoint)));
            }
        }

        /// <summary>
        /// 检查相同IP是否超过设定
        /// </summary>
        /// <param name="clientSocket"></param>
        /// <returns>返回true时禁止连接</returns>
        private bool CheckHasMaxSameIp(Socket clientSocket)
        {
            var iep = clientSocket.RemoteEndPoint as IPEndPoint;
            if (iep == null)
                return false;

            if (iep.Address.ToString().Substring(0, 7) == "127.0.0") //本机不限制
                return false;

            var sameIpCount = SessionTable.Values.Count(session => session.EndPoint != null && Equals(session.EndPoint.Address, iep.Address));
            return sameIpCount > SameIpMaxConnections;
        }


        #endregion

        #endregion

        #endregion



        #region 发送至  SendTo

        /// <summary>
        /// 发送至某一客户端
        /// </summary>
        /// <param name="channel">对应的客户端</param>
        /// <param name="datagram"></param>
        public void SendTo(TcpChannel channel, byte[] datagram)
        {
            if (channel == null || datagram == null || datagram.Length == 0) return;
            channel.Send(datagram);
        }
        /// <summary>
        /// 发送至某一客户端
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="datagram"></param>
        public void SendTo(long clientId, byte[] datagram)
        {
            if (datagram == null || datagram.Length == 0) return;
            var channel = SessionTable[clientId];
            channel.Send(datagram);
        }

        /// <summary>
        /// 发送至所有客户端
        /// </summary>
        public void SendToAll(byte[] datagram)
        {
            if (datagram == null || datagram.Length == 0) return;
            var arrChannel = SessionTable.Values.ToArray();
            foreach (var channel in arrChannel)
            {
                channel.Send(datagram);
            }
        }

        #endregion



        #region 关闭会话 CloseSession   CloseAllSession  TODO:事件没有引发，待定

        /// <summary>
        /// 关闭会话
        /// </summary>
        /// <param name="clientId"></param>
        public void CloseSession(long clientId)
        {
            var channel = SessionTable[clientId];
            RemoveChannel(channel);
        }
        /// <summary>
        /// 关闭会话
        /// </summary>
        /// <param name="channel"></param>
        public void CloseSession(ITcpChannel channel)
        {
            RemoveChannel(channel);
        }

        /// <summary>
        /// 关闭所有会话
        /// </summary>
        public void CloseAllSession()
        {
            var channels = SessionTable.Values.ToArray();
            foreach (var channel in channels)
            {
                RemoveChannel(channel);
            }
            SessionTable.Clear();
        }


        #region 内部方法



        #endregion


        #endregion


        #endregion


        #region 事件定义  及事件捕获

        /// <summary>
        /// 客户端连入中 事件
        /// <remarks>通过该事件能对一些连入进行限制处理，比如IP限制，数量限制，等</remarks>
        /// </summary>
        public event EventHandler<ConnectingEventArgs> Connecting;
        /// <summary>
        /// 客户端连入中 调用
        /// </summary>
        /// <param name="eventArgs"></param>
        protected virtual void OnConnecting(ConnectingEventArgs eventArgs)
        {
            if (Connecting == null) return;
            Connecting(this, eventArgs);
        }
        /// <summary>
        /// 客户端连入成功 事件
        /// </summary>
        public event EventHandler<ChannelEventArgs> Connected;
        /// <summary>
        /// 客户端连入成功 调用
        /// </summary>
        /// <param name="channel"></param>
        protected virtual void OnConnected(ITcpChannel channel)
        {
            try
            {
                AddChannel(channel);
                //事件通知
                if (Connected == null) return;
                Connected(this, new ChannelEventArgs(channel));
                channel.BeginReceive();//开始接受客户端发来的数据
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
        }


        ///// <summary>
        ///// 准备断开连接事件
        ///// </summary>
        //public event EventHandler<ChannelDisconnectEventArgs> ClientDisConnecting;
        ///// <summary>
        ///// 准备断开连接时调用
        ///// </summary>
        //protected virtual void OnDisconnecting(object sender, ChannelDisconnectEventArgs e)
        //{
        //    try
        //    {
        //        RemoveChannel(e.Channel);
        //        if (ClientDisConnecting == null) return;
        //        ClientDisConnecting(sender, e);
        //    }
        //    catch (Exception ex)
        //    {
        //        OnException(ex);
        //    }
        //}

        /// <summary>
        /// 连接断开 事件
        /// </summary>
        public event EventHandler<ChannelDisconnectEventArgs> ClientDisconnected;
        /// <summary>
        /// 连接断开 调用
        /// </summary>
        protected virtual void OnClientDisconnected(object sender, ChannelDisconnectEventArgs e)
        {
            try
            {
                Tracer.Debug("Removing.");
                RemoveChannel(e.Channel); //移除会话
                if (ClientDisconnected == null) return;
                ClientDisconnected(sender, e);
                Tracer.Debug("Removinged.");
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
        }


        ///// <summary>
        ///// 收到数据 事件
        ///// <remarks>
        ///// SocketAsyncEventArgs.UserToken中携带有客户端Id，相关信息
        ///// </remarks>
        ///// </summary>
        //public event EventHandler<SocketAsyncEventArgs> DataReceived;
        ///// <summary>
        ///// 收到数据  时调用
        ///// </summary>
        ///// <param name="tcpChannel"></param>
        ///// <param name="e"></param>
        //protected virtual void OnDataReceived(object tcpChannel, SocketAsyncEventArgs e)
        //{
        //    if (DataReceived == null) return;
        //    DataReceived(tcpChannel, e);
        //}

        /// <summary>
        /// 异常事件
        /// </summary>
        public event EventHandler<TcpExceptionEventArgs> TcpException;
        /// <summary>
        /// TcpServer异常发生时调用
        /// </summary>
        /// <param name="ex"></param>
        private void OnException(Exception ex)
        {
            if (TcpException == null) return;
            TcpException(this, new TcpExceptionEventArgs(ex));
        }
        /// <summary>
        /// 异常发生时调用
        /// </summary>
        protected virtual void OnException(TcpExceptionEventArgs e)
        {
            if (TcpException == null) return;
            TcpException(this, e);
        }
        /// <summary>
        /// 异常发生时调用
        /// </summary>
        protected virtual void OnException(object sender, Exception ex)
        {
            if (TcpException == null) return;
            TcpException(sender, new TcpExceptionEventArgs(ex));
        }
        /// <summary>
        /// 异常发生时调用
        /// </summary>
        /// <param name="sender">事件来源</param>
        /// <param name="ex">异常</param>
        protected virtual void OnException(object sender, TcpExceptionEventArgs ex)
        {
            if (TcpException == null) return;
            TcpException(sender, ex);
        }


        #endregion


    }
}